from eden.evo.aminos import NullAmino, Amino
from eden.evo.operands import ConstOperand, RegisterOperand
from eden.evo.protein import Protein
import doctest

class Translator:
    def __init__(self, encoding):
        self.__encoding = encoding
        
    @staticmethod
    def get_predefined_encoding():
        '''
        >>> encoding = Translator.get_predefined_encoding()
        >>> encoding[0].__name__
        'NullAmino'
        >>> len(encoding) == len(Amino.get_predefined_aminos())
        True
        '''
        return Translator.get_encoding(Amino.get_predefined_aminos())
    
    @staticmethod
    def get_encoding(amino_classes):
        '''
        >>> from eden.evo.aminos import *
        >>> encoding = Translator.get_encoding([AddAmino, SubAmino])
        >>> encoding.keys()
        [0, 1, 2]
        >>> [v.__name__ for (k, v) in encoding.items()]
        ['NullAmino', 'AddAmino', 'SubAmino']
        
        >>> encoding = Translator.get_encoding([AddAmino, NullAmino, SubAmino])
        >>> encoding.keys()
        [0, 1, 2]
        >>> [v.__name__ for (k, v) in encoding.items()]
        ['NullAmino', 'AddAmino', 'SubAmino']
        '''
        encoding = {}
        encoding[0] = NullAmino
        if NullAmino in amino_classes:
            amino_classes.remove(NullAmino)
        for amino_class in amino_classes:
            encoding[len(encoding)] = amino_class
        return encoding
        
    def translate(self, chromosome):
        '''
        >>> from eden.evo.aminos import *
        >>> from eden.evo.chromosome import *
        >>> from eden.evo.codon import *
        >>> mapping = { 1 : AddAmino }
        
        chromosome that has NOT been translated before
        >>> chromosome = Chromosome((Codon(1, 11, 22), ))
        >>> protein = Translator(mapping).translate(chromosome)
        >>> len(protein)
        1
        >>> isinstance(protein.getamino(0), AddAmino)
        True
        >>> chromosome.protein == protein
        True
        
        chromosome that has been translated before
        >>> chromosome = Chromosome((Codon(1, 11, 22), ))
        >>> chromosome.protein = Protein(())
        >>> protein = Translator(mapping).translate(chromosome)
        >>> chromosome.protein == protein
        True
        '''
        if hasattr(chromosome, 'protein'):
            return chromosome.protein
        aminos = (self.translate_codon(codon) for codon in chromosome)
        protein = Protein(aminos)
        chromosome.protein = protein
        return protein
        
    def translate_codon(self, codon):
        '''
        >>> from eden.evo.aminos import *
        >>> from eden.evo.codon import *
        >>> mapping = { 1 : AddAmino }
        
        codon that has NOT been translated before
        >>> codon = Codon(1, 11, 22)
        >>> amino = Translator(mapping).translate_codon(codon)
        >>> isinstance(amino, AddAmino)
        True
        >>> isinstance(amino.getleft(), ConstOperand)
        True
        >>> amino.getleft().getvalue(None)
        11
        >>> isinstance(amino.getright(), ConstOperand)
        True
        >>> amino.getright().getvalue(None)
        22
        >>> codon.amino == amino
        True

        codon that has been translated before
        >>> codon = Codon(1, 11, 22)
        >>> codon.amino = SubAmino(RegisterOperand(33), ConstOperand(44))
        >>> amino = Translator(mapping).translate_codon(codon)
        >>> codon.amino == amino
        True
        '''
        if hasattr(codon, 'amino'):
            return codon.amino
        left = self.translate_operand(codon.loperand)
        right = self.translate_operand(codon.roperand)
        amino = self.translate_amino(codon.operator, left, right)
        codon.amino = amino
        return amino
    
    def translate_amino(self, base, left, right):
        '''
        >>> from eden.evo.aminos import *
        >>> mapping = { 0 : NullAmino, 1 : AddAmino }
        
        translate according to mapping
        >>> amino = Translator(mapping).translate_amino(1, None, None)
        >>> isinstance(amino, AddAmino)
        True
        '''
        amino_class = self.__encoding[base]
        return amino_class(left, right)
    
    def translate_operand(self, base):
        '''
        11 --> ConstOperand(11)
        >>> operand = Translator(None).translate_operand(11)
        >>> isinstance(operand, ConstOperand)
        True
        >>> operand.getvalue(None)
        11
        
        129 --> RegisterOperand(0)
        >>> operand = Translator(None).translate_operand(129)
        >>> isinstance(operand, RegisterOperand)
        True
        >>> operand.getindex()
        1
        '''
        return ConstOperand.getinstance(base) if base < 128 else RegisterOperand.getinstance(base - 128)
    
if __name__ == '__main__':
    doctest.testmod()